﻿#ifndef _CELLSERVER_H_
#define _CELLSERVER_H_

#include "CELL.h"
#include "CELLClient.hpp"
#include <map>
#include <vector>
#include <thread>
#include <mutex>
#include "CELLTask.hpp"
#include "CELLTimer.hpp"

class INetEvent;

/*
	接受客户端的消息的服务
*/
class CELLServer
{
public:
	CELLServer() {}

	/*
		析构函数设置为虚函数，本EasyTcpSever中存储消息服务线程的类是使用
		多态的形式，调用析构函数的时候设置析构函数为虚函数就会先调用子类的析构
		函数，在子类的析构函数中调用父类(本类)的Close函数去终止本线程，
		就会防止DoSelect纯虚函数被调用
	*/
	virtual ~CELLServer()
	{
		CELLLog_Info("CELLServer %d Close ===== begin", _id);
		Close();
		CELLLog_Info("CELLServer %d Close ===== end", _id);
	}

	// 设置网络事件，在其中设置的是EasyTcpServer对象，用于触发INetEvent类中的事件
	void SetEventObj(INetEvent* event)
	{
		_pNetEvent = event;
	}

	// 处理网络消息 线程入口函数
	void OnRun(CELLThread* pthread)
	{
		while (pthread->isRun())
		{
			if (!_clientsBuff.empty())
			{
				// 这里的锁是保证接受新链接线程和接受消息服务线程安全
				std::lock_guard<std::mutex> lock(_mutex);
				for (auto pClient : _clientsBuff)
				{
					// 将客户端缓冲队列中的客户端添加到正式客户端队列中
					_clients[pClient->sockfd()] = pClient;
					pClient->id = _id;
					if (_pNetEvent)
						_pNetEvent->OnNetJoin(pClient);
					OnClientJoin(pClient);
				}
				_clientsBuff.clear();
				_clients_change = true;
			}

			if (_clients.empty())
			{
				CELLThread::Sleep(1);
				// 如果没有客户端，也要更新旧的时间戳
				_old_time = CELLTime::getNowInMilliSec();
				continue;
			}

			// 心跳检测
			CheckTime();

			if (!DoNetEvents())
			{
				// select错误
				pthread->Exit();
				break;
			}

			DoMsg();
		}
		CELLLog_Info("CELLServer %d OnRun exit", _id);
		return;
	}

	/*
		当主动退出服务端后本类的子类会先被释放，实现的纯虚函数也会
		被释放，如果此时OnRun函数在调用，此时调用的就是纯虚函数，
		就会报错退出
	*/
	virtual bool DoNetEvents() = 0;

	// 处理网络消息
	void DoMsg()
	{
		CELLClient* pClient = nullptr;
		for (auto itr : _clients)
		{
			pClient = itr.second;
			while (pClient->hasMsg())
			{
				// 处理最前面的消息
				OnNetMsg(pClient, pClient->front_msg());
				// 移除这条消息(删除接收缓冲区最前的一条消息)
				pClient->pop_front_msg();
			}
		}
	}

	// 客户端离开
	void OnClientLeave(CELLClient* pClient)
	{
		if (_pNetEvent)
			_pNetEvent->OnNetLeave(pClient);
		_clients_change = true;
		delete pClient;
	}

	// 新客户端加入
	virtual void OnClientJoin(CELLClient* pClient) {}

	// 接收数据 处理粘包 拆包(IOCP是不会触发这个方法的)
	int RecvData(CELLClient* pClient)
	{
		// 接受客户端数据
		int nlen = pClient->RecvData();
		if (nlen <= 0)
		{
			return SOCKET_ERROR;
		}
		// 触发<接收到网络事件>事件
		if (_pNetEvent)
			_pNetEvent->OnNetRecv(pClient);
		return nlen;
	}

	// 检测本类中所有正式客户端队列的心跳
	void CheckTime()
	{
		auto tNow = CELLTime::getNowInMilliSec();
		// 得到每次检测所有客户端心跳时的时间戳
		auto dt = tNow - _old_time;
		// 更新上一次时间戳
		_old_time = tNow;
		CELLClient* pClient = nullptr;
		// 在循环中不改变迭代器指针，在循环中会删除元素，在这之间进行改变
		for (auto iter = _clients.begin(); iter != _clients.end(); )
		{
			pClient = iter->second;
			// 心跳检测
			if (pClient->checkHeart(dt))
			{
#ifdef CELL_USE_IOCP
				// 使用IOCP模式下面，当客户端被心跳检测移除时，客户端暂时不能被释放(OnCLientLeave)
					// 因为 提交了接收数据或发送数据的任务 关闭套接字后IOCP会知晓并反应,
					// (反应会使用到client的IO_DATA_BASE)
				if (pClient->isPostIoAction())
					pClient->destory(); // 先关闭socket就可以，删除client交给IOCP
				else
					// 没有提交接收或者发送任务关闭sockfdIOCP不会收到通知
					OnClientLeave(pClient);
#else
				// select或epoll模式下：客户端已死亡, 触发客户端离开事件, 移除本客户端
				OnClientLeave(pClient);
#endif // #ifdef CELL_USE_IOCP

				// 在这里进行迭代器指针的自增(后置++，先赋值再自增)
				auto iterOld = iter++;
				_clients.erase(iterOld);
				continue;
			}
			// 定时发送数据检测
			// iter->second->checkSend(dt);
			// 在这里进行迭代器指针的自增
			++iter;
		}
	}

	// 处理消息，响应网络消息
	void OnNetMsg(CELLClient* pCLient, DataHeader* header)
	{
		if (_pNetEvent)
			_pNetEvent->OnNetMsg(this, pCLient, header);
	}

	// 接收消息(这个方法是用于IOCP的接收计数: IOCP完成接收数据的任务调用)
	void OnNetRecv(CELLClient* pClient)
	{
		if (_pNetEvent)
			_pNetEvent->OnNetRecv(pClient);
	}

	// 关闭socket
	void Close()
	{
		// _taskServer.Close();
		_thread.Close();
	}

	// 操作客户端缓冲队列
	void AddClient(CELLClient* pClient)
	{
		// 这里的锁是保证接受新链接线程和接受消息服务线程安全
		std::lock_guard<std::mutex> lock(_mutex);
		_clientsBuff.push_back(pClient);
	}

	// 线程启动函数
	void Start()
	{
		// 启动任务服务线程，用于添加耗io操作的任务
		// _taskServer.Start();
		_thread.Start(
			// onCreate
			nullptr,
			// onRun
			[this](CELLThread* pthread) {
				OnRun(pthread);
			},
			// onDestory
			[this](CELLThread* pthread) {
				ClearClients();
			}
		);
	}

	// 获取客户端队列长度
	size_t GetClientCount()
	{
		return _clientsBuff.size() + _clients.size();
	}

	// 释放正式客户端和缓冲客户端队列空间
	void ClearClients()
	{
		for (auto iter : _clients)
		{
			delete iter.second;
		}
		_clients.clear();

		for (auto iter : _clientsBuff)
		{
			delete iter;
		}
		_clientsBuff.clear();
	}

	// 添加发送消息服务
	void AddSendTask(CELLClient* pClient, DataHeader* header)
	{
		// 任务加入一个匿名函数
		/*_taskServer.AddTask([pClient, header]() {
			pClient->SendData(header);
		});*/
	}

	// 设置id
	void SetId(int id)
	{
		_id = id;
		// _taskServer.serverId = id;
	}

	// 注册时间事件，单位(秒)
	unsigned int add_time_event(CELLTimer::time_callback cb, void *ctx, unsigned int after_sec, int repeat = 1)
	{
		return _time_events.register_time_event(cb, ctx, after_sec, repeat);
	}

	// 更新时间事件，单位(秒)
	void mod_time_event(unsigned int timerid, unsigned int after_sec, int repeat = 1)
	{
		_time_events.update_time_event(timerid, after_sec, repeat);
	}

	// 删除时间事件，单位(秒)
	void del_time_event(unsigned int timerid)
	{
		_time_events.unregister_time_event(timerid);
	}

protected:
	// 定时事件处理
	CELLTimer _time_events;
	// 正式客户端队列
	std::map<SOCKET, CELLClient*> _clients;
	// 客户端列表是否有变化
	bool _clients_change = true;
	// 本接收消息服务id
	int _id = -1;
private:
	// 客户端缓冲队列
	std::vector<CELLClient*> _clientsBuff;
	// 客户端缓冲队列的锁
	std::mutex _mutex;
	// 网络事件对象（指向了EasyTcpServer对象，用于调用这个对象中onNetLeave，OnNetMsg等事件处理函数）
	INetEvent* _pNetEvent = nullptr;
	// 本类创建时被赋值为当前时间戳
	time_t _old_time = CELLTime::getNowInMilliSec();
	// 描述本类工作线程信息
	CELLThread _thread;
	// 用于执行一些耗io的操作
	// CellTaskServer _taskServer;
};

#endif // #ifndef _CELLSERVER_H_
